/**
 * @file
 * @brief		This is a brief description.
 * @details	    This is the detail description.
 * @author		author
 * @date		    date
 * @version	    v1.0
 * @par Copyright(c): 	abc corporation
 * @par History:
 *	version: author, date, desc\n
 */

/*-----------------------------------------------------------------------------
 * HEADER FILES
 *---------------------------------------------------------------------------*/
#include "gmi_spi.h"

#include <stdint.h>
#include <stdlib.h>

#include "gmi_log.h"
#include "gmi_time.h"

#ifdef __GMI_STM32__
#include "main.h"
#endif

/*-----------------------------------------------------------------------------
 * ENUM
 *---------------------------------------------------------------------------*/
enum gmi_spi_device_e
{
    GPIO_SIMULATED_SPI_1,
    GPIO_SIMULATED_SPI_2,
    PERIPHERAL_SPI_1,
    PERIPHERAL_SPI_2,
    PERIPHERAL_SPI_3
};

static gmi_time_t* h_time = NULL;

/*-----------------------------------------------------------------------------
 * STATIC FUNCTIONS DEFINITIONS
 *---------------------------------------------------------------------------*/

/**
 * @brief GPIO 模拟 SPI 的 bit 写入
 * @param {gmi_gpio_simulated_spi_t*} spi
 * @param {gmi_pin_level_t} level
 * @return {*}
 */
static void gmi_spi_write_bit(gmi_gpio_simulated_spi_t* spi,
                              gmi_pin_level_t level)
{
    if (spi->cpha == GMI_SPI_CPHA_2ND_EDGE)
    {
        gmi_pin_set_level(&(spi->sclk), LOW_LEVEL);
        gmi_pin_set_level(&(spi->sdio), level);
        gmi_sleep_us(h_time, spi->delay_slices);
        gmi_pin_set_level(&(spi->sclk), HIGH_LEVEL);
        gmi_sleep_us(h_time, spi->delay_slices);
    }
    else
    {
        gmi_pin_set_level(&(spi->sclk), HIGH_LEVEL);
        gmi_pin_set_level(&(spi->sdio), level);
        gmi_sleep_us(h_time, spi->delay_slices);
        gmi_pin_set_level(&(spi->sclk), LOW_LEVEL);
        gmi_sleep_us(h_time, spi->delay_slices);
    }
}

/**
 * @brief GPIO 模拟 SPI 的 byte 写入
 * @param {gmi_gpio_simulated_spi_t*} spi
 * @param {uint8_t} data
 */
static void gmi_spi_write_byte(gmi_gpio_simulated_spi_t* spi, uint8_t data)
{
    for (int i = 7; i >= 0; i--)
    {
        gmi_spi_write_bit(spi, (data >> i) & 0x01 ? HIGH_LEVEL : LOW_LEVEL);
    }
}

/**
 * @brief GPIO 模拟 SPI 的 byte 读取
 * @param {gmi_gpio_simulated_spi_t*} spi
 * @return {*}
 */
static uint8_t gmi_spi_read_byte(gmi_gpio_simulated_spi_t* spi)
{
    uint8_t data = 0;

    for (int i = 7; i >= 0; i--)
    {
        if (spi->cpha == GMI_SPI_CPHA_2ND_EDGE)
        {
            gmi_pin_set_level(&(spi->sclk), LOW_LEVEL);
            data |= gmi_pin_get_level(&(spi->sdio)) << i;
            gmi_sleep_us(h_time, spi->delay_slices);

            gmi_pin_set_level(&(spi->sclk), HIGH_LEVEL);
            gmi_sleep_us(h_time, spi->delay_slices);
        }
        else
        {
            gmi_pin_set_level(&(spi->sclk), HIGH_LEVEL);
            gmi_sleep_us(h_time, spi->delay_slices);

            gmi_pin_set_level(&(spi->sclk), LOW_LEVEL);
            data |= gmi_pin_get_level(&(spi->sdio)) << i;
            gmi_sleep_us(h_time, spi->delay_slices);
        }
    }

    return data;
}

GMI_STATUS gmi_hal_spi_read_data(gmi_gpio_simulated_spi_t* spi,
                                 uint8_t* buffer,
                                 size_t size_to_transfer)
{
    GMI_STATUS status = GMI_STATUS_OK;

    /*
     * 需要根据CPOL设定值来设置SCLK电平
     */
    gmi_pin_set_level(&(spi->sclk),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? LOW_LEVEL : HIGH_LEVEL);
    gmi_pin_set_dir(&(spi->sdio), GMI_GPIO_INPUT);

    *buffer = gmi_spi_read_byte(spi);

    // ! 设置 CPOL值
    gmi_pin_set_level(&(spi->sclk),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);
    gmi_pin_set_dir(&(spi->sdio), GMI_GPIO_OUTPUT);

    return status;
}

GMI_STATUS gmi_hal_spi_write_data(gmi_gpio_simulated_spi_t* spi,
                                  uint8_t* buffer,
                                  size_t size_to_transfer)
{
    GMI_STATUS status = GMI_STATUS_OK;

    gmi_pin_set_dir(&(spi->sdio), GMI_GPIO_OUTPUT);  // 设置为输出模式
    gmi_pin_set_level(&(spi->sclk),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);
    gmi_sleep_us(h_time, spi->delay_slices);

    for (size_t i = 0; i < size_to_transfer; i++)
    {
        gmi_spi_write_byte(spi, *(buffer + i));
    }

    return status;
}

GMI_STATUS gmi_reg_read(gmi_gpio_simulated_spi_t* spi,
                        uint8_t regAddr,
                        uint8_t* regData)
{
    GMI_STATUS status = GMI_STATUS_OK;

    uint8_t tmp = regAddr & 0x7F;

    status = gmi_hal_spi_write_data(spi, &tmp, 1);
    CHECK_STATUS(status);

    status = gmi_hal_spi_read_data(spi, regData, 1);
    CHECK_STATUS(status);

    gmi_pin_set_level(&(spi->sclk),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);
    gmi_pin_set_level(&(spi->sdio),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);

    return status;
}

GMI_STATUS gmi_reg_write(gmi_gpio_simulated_spi_t* spi,
                         uint8_t regAddr,
                         uint8_t regData)
{
    GMI_STATUS status = GMI_STATUS_OK;

    uint8_t tmp[2] = { (uint8_t)(regAddr | 0x80), regData};

    status = gmi_hal_spi_write_data(spi, tmp, 2);
    CHECK_STATUS(status);

#ifdef SPI_REG_VERIFICATION
    uint8_t temp = 0;
    status       = gmi_reg_read(spi, regAddr, &temp);
    CHECK_STATUS(status);

    if (temp != regData)
    {
        LOGE("Failed at writting data to 0x%02x.", regAddr);
        status = ERR_IO;
    }
#endif

    gmi_pin_set_level(&(spi->sclk),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);
    gmi_pin_set_level(&(spi->sdio),
                      spi->cpol == GMI_SPI_CPOL_HIGH ? HIGH_LEVEL : LOW_LEVEL);

    return status;
}

/*-----------------------------------------------------------------------------
 * FUNCTIONS DEFINITIONS
 *---------------------------------------------------------------------------*/

GMI_STATUS gmi_spi_init(GMI_SPI* spi)
{
    GMI_STATUS status = GMI_STATUS_OK;

    if (h_time == NULL)
    {
        gmi_log_info("time instance haven't initialize!");
        h_time = gmi_time_init();
    }

    return status;
}

/**
 * @brief 销毁虚拟SPI外设实例
 * @param {GMI_SPI} *spi
 * @return {*}
 */
GMI_STATUS gmi_spi_deinit(GMI_SPI* spi)
{
    GMI_STATUS status = GMI_STATUS_OK;
    CHECK_NULL(spi);

    return status;
}

GMI_STATUS gmi_spi_transmit(GMI_SPI* spi, uint8_t* data_buffer, uint32_t size)
{
    GMI_STATUS status = GMI_STATUS_OK;
    CHECK_NULL(spi);

    return status;
}

GMI_STATUS gmi_spi_receive(GMI_SPI* spi, uint8_t* data_buffer, uint32_t size)
{
    GMI_STATUS status = GMI_STATUS_OK;
    CHECK_NULL(spi);

#ifdef __GMI_STM32__
    status = (GMI_STATUS)HAL_SPI_Receive((SPI_HandleTypeDef*)spi, data_buffer, size, 100U);
#endif

    return status;
}
